﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Media3D;
using System.Windows.Input;
using System.ComponentModel;

using Microsoft.Practices.Unity;
using Microsoft.Win32;

using ObjectEditor;
using ObjectEditor.Display3D;
using ObjectEditor.Editor;

using EQEmu.ZonePoints.Display3D;
using ZP = EQEmu.ZonePoints;
using EQEmu.Database;

namespace EQEmu.ZonePointsPlugin
{
    internal class ZonePointsVisual3D : ModelVisual3D
    {
    }

    public class ZonePointsRibbonTabViewModel : ViewModel, IWorldModel3DProvider, IObjectEditor, IDisposable
    {
        public event EventHandler<ModelChangedEventArgs> ModelChanged;
        public event EventHandler<CameraChangeEventArgs> RequestCameraChange;
        public event EventHandler<ObjectSelectedEventArgs> ObjectSelected;

        private Transform3D _transform;

        private string _zoneShortName;
        private int _zoneVersion;
        private ZP.ZonePoints _zonePoints;
        private ZonePointsDisplay3D _display3d;
        private ModelVisual3D _modelVisual;
        private zone_points _selectedZp;
        private double _zAdjustment;
        private bool _useDatabase;
        //private IEnumerable<zone_points> _multipleSelections = null;

        private DelegateCommand _saveFileCommand;
        private DelegateCommand _openFileCommand;
        private DelegateCommand _searchZoneCommand;
        private DelegateCommand _removeZPCommand;
        private DelegateCommand _addZPCommand;
        private DelegateCommand _commitChangesCommand;

        public ZonePointsRibbonTabViewModel()
        {
            ZoneVersion = 0;
            ZAdjustment = 2.0;
        }

        [Dependency]
        public EQEmuContextFactory EntitiesFactory
        {
            get;
            set;
        }
        
        public DelegateCommand CommitChangesCommand
        {
            get
            {
                if (_commitChangesCommand == null)
                {
                    _commitChangesCommand = new DelegateCommand(
                        x =>
                        {
                            throw new NotImplementedException();
                        },
                        y =>
                        {
                            return (_zonePoints as ZP.ZonePointsDatabase) != null;
                        });
                }
                return _commitChangesCommand;
            }
        }

        public DelegateCommand CreateZPCommand
        {
            get
            {
                if (_addZPCommand == null)
                {
                    _addZPCommand = new DelegateCommand(
                        x =>
                        {
                            var zp = _zonePoints.CreatePoint();
                            _zonePoints.AddPoint(zp);
                            SelectedZP = zp;
                            //_multipleSelections = null;
                        },
                        y =>
                        {
                            return _zonePoints != null;
                        });
                }
                return _addZPCommand;
            }
        }

        public DelegateCommand RemoveZPCommand
        {
            get
            {
                if (_removeZPCommand == null)
                {
                    _removeZPCommand = new DelegateCommand(
                        x =>
                        {
                            if (_selectedZp != null)
                            {
                                _zonePoints.RemovePoint(_selectedZp);
                            }
                            SelectedZP = null;
                        },
                        y =>
                        {
                            return _zonePoints != null && _selectedZp != null;
                        });
                }
                return _removeZPCommand;
            }
        }

        public DelegateCommand SaveFileCommand
        {
            get
            {
                if (_saveFileCommand == null)
                {
                    _saveFileCommand = new DelegateCommand(
                        x =>
                        {
                            throw new NotImplementedException();
                        },
                        y =>
                        {
                            return _zonePoints != null;
                        });
                }
                return _saveFileCommand;
            }
        }

        public DelegateCommand OpenFileCommand
        {
            get
            {
                if (_openFileCommand == null)
                {
                    _openFileCommand = new DelegateCommand(
                        x =>
                        {
                            throw new NotImplementedException();
                        },
                        y =>
                        {
                            return true;
                        });
                }
                return _openFileCommand;
            }
        }

        public DelegateCommand SearchZoneCommand
        {
            get
            {
                if (_searchZoneCommand == null)
                {
                    _searchZoneCommand = new DelegateCommand(
                        x =>
                        {
                            LoadZoneData();
                        },
                        y =>
                        {
                            return true;
                        });
                }
                return _searchZoneCommand;
            }
        }

        private ZP.ZonePoints ZonePoints
        {
            get { return _zonePoints; }
            set
            {
                if (value != null)
                {
                    if (_zonePoints != null)
                    {
                        var disposer = _zonePoints as IDisposable;
                        if (disposer != null) disposer.Dispose();
                    }

                    _zonePoints = value;
                    SaveFileCommand.RaiseCanExecuteChanged();
                    if (_display3d != null)
                    {
                        _display3d.Dispose();
                    }
                    _display3d = new ZonePointsDisplay3D(_zonePoints);
                    Model3D = new ZonePointsVisual3D()
                    {
                        Content = _display3d.Model,
                        Transform = Transform3D
                    };

                    ZoneShortName = value.ZoneShortName;
                    ZoneVersion = value.ZoneVersion;
                }
                CreateZPCommand.RaiseCanExecuteChanged();
                CommitChangesCommand.RaiseCanExecuteChanged();
            }
        }

        public bool UseDatabase
        {
            get { return _useDatabase; }
            set
            {
                _useDatabase = value;
                OnPropertyChanged("UseDatabase");
            }
        }

        public int ZoneVersion
        {
            get { return _zoneVersion; }
            set
            {
                _zoneVersion = value;
                OnPropertyChanged("ZoneVersion");
            }
        }

        private void LoadZoneData()
        {
            if (_zonePoints != null && _zonePoints.HasModifications)
            {
                var result =
                    System.Windows.MessageBox.Show("Discard all changes?", "Discard channges", System.Windows.MessageBoxButton.YesNo);
                if (result == System.Windows.MessageBoxResult.No) return;
            }

            if (UseDatabase)
            {
                try
                {
                    ZonePoints = new ZP.ZonePointsDatabase(ZoneShortName, this.EntitiesFactory, ZoneVersion);
                }
                catch (Exception e)
                {
                    System.Windows.MessageBox.Show("Could not created object:" + e.Message + Environment.NewLine + e.StackTrace);
                }
            }
            else
            {
                ZonePoints = new ZP.ZonePoints(ZoneShortName, ZoneVersion);
            }
        }

        public string ZoneShortName
        {
            get { return _zoneShortName; }
            set
            {
                _zoneShortName = value;
                OnPropertyChanged("ZoneShortName");
            }
        }

        public double ZAdjustment
        {
            get { return _zAdjustment; }
            set
            {
                _zAdjustment = value;
                OnPropertyChanged("ZAdjustment");
            }
        }

        public zone_points SelectedZP
        {
            get { return _selectedZp; }
            set
            {
                _selectedZp = value;

                OnPropertyChanged("SelectedZP");
                if (value != null)
                {
                    var point = new Point3D(value.x,value.y,value.z);                    
                    var look = Transform3D.Transform(point);

                    OnObjectSelected(value as INotifyPropertyChanged, null);
                    OnCameraChange(new CameraChangeEventArgs()
                    {
                        LookAtPosition = look
                    });
                }
                else
                {
                    OnObjectSelected(null, null);
                }
                RemoveZPCommand.RaiseCanExecuteChanged();
            }
        }

        [Dependency("EQEmu.Transform")]
        public Transform3D Transform3D
        {
            get { return _transform; }
            set
            {
                _transform = value;
            }
        }

        public ModelVisual3D Model3D
        {
            get { return _modelVisual; }
            private set
            {
                if (_modelVisual != null)
                {
                    _modelVisual.Children.Clear();
                }

                _modelVisual = value;
                OnPropertyChanged("Model3D");
                OnModelChanged();
            }        
        }

        protected void OnModelChanged()
        {
            var e = ModelChanged;
            if (e != null)
            {
                e(this, new ModelChangedEventArgs());
            }
        }

        protected void OnObjectSelected(INotifyPropertyChanged obj,IEnumerable<INotifyPropertyChanged> objs)
        {
            var e = ObjectSelected;
            if (e != null)
            {
                e(this, new ObjectSelectedEventArgs(obj,objs));
            }
        }

        protected void OnCameraChange(CameraChangeEventArgs change)
        {
            var e = RequestCameraChange;
            if (e != null)
            {
                e(this, change);
            }
        }

        public void UserClicked(System.Windows.Media.Media3D.Point3D point, AppInformation info)
        {
            if (info.SelectedRibbonTab == null || (info.SelectedRibbonTab as ZonePointsRibbonTab) == null) return;
            if (_zonePoints == null) return;

            var p = Transform3D.Transform(point);

            if ((Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt)) && SelectedZP != null)
            {
                var zp = SelectedZP;
                zp.x = (float)(p.X);
                zp.y = (float)(p.Y);
                zp.z = (float)(p.Z + ZAdjustment);
            }
            else if ((Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) && SelectedZP != null)
            {
                var zp = SelectedZP;
                zp.target_x = (float)(p.X);
                zp.target_y = (float)(p.Y);
                zp.target_z = (float)(p.Z + ZAdjustment);
            }
            else
            {
                var zp = _zonePoints.FindClosestZonePoint(p.X, p.Y, p.Z,_display3d.BoxSize);
                if (zp != null)
                {
                    SelectedZP = zp;
                }
            }
        }

        public void ConstrainView(ViewConstraints constraints)
        {
            if (_display3d == null) return;

            _display3d.UpdateView(new EQEmu.Display3D.ViewConstraints()
            {
                MaxX = constraints.MaxX,
                MaxY = constraints.MaxY,
                MaxZ = constraints.MaxZ,
                MinX = constraints.MinX,
                MinY = constraints.MinY,
                MinZ = constraints.MinZ
            });
        }

        protected virtual void Dispose(bool disposeAll)
        {
            if (_display3d != null)
            {
                _display3d.Dispose();
            }

            if (_zonePoints != null)
            {
                var disposer = _zonePoints as IDisposable;
                if (disposer != null) disposer.Dispose();
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }
    }
}
